﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MoneyWentWhere.DataModel
{
    class ExchangeRate : IComparable
    {
        readonly public DateTime Start;
        readonly public Currency Source;
        readonly public Currency Dest;
        readonly public decimal Rate;

        readonly public Transaction SourceTransfer;

        public ExchangeRate(DateTime start, Currency src, Currency dest, decimal rate)
        {
            Start = start;
            Source = src;
            Dest = dest;
            Rate = rate;
        }

        public ExchangeRate(Transaction sourceTransfer)
        {
            if (sourceTransfer.TransferToTx == null) { throw new ArgumentException("Not a transfer tx: " + sourceTransfer); }

            SourceTransfer = sourceTransfer;

            Start = sourceTransfer.Date;
            Source = sourceTransfer.Account.Currency;
            Dest = sourceTransfer.TransferToTx.Account.Currency;

            Rate = Currency.ExchangeRate(sourceTransfer.Amount, 
                sourceTransfer.TransferToTx.Amount);
        }

        public static ExchangeRate OneToOne(Currency src, Currency dest)
        {
            return new ExchangeRate(DateTime.MinValue, src, dest, 1);
        }

        public override bool Equals(object obj)
        {
            ExchangeRate that = obj as ExchangeRate;
            if (that == null) return false;

            return Start.Equals(that.Start) &&
                Source.Equals(that.Source) &&
                Dest.Equals(that.Dest) &&
                Rate.Equals(that.Rate);
        }

        public int CompareTo(object obj)
        {
            ExchangeRate that = obj as ExchangeRate;
            if (that == null) return -1;
            return this.Start.CompareTo(that.Start);
        }

        public override string ToString()
        {
            return Start + ": " + Source + ">" + Dest + " " + Rate;
        }
    }

    class ExchangeRatesFromCurrency
    {
        readonly public Currency Source;

        // destination currency, rates sorted by month 
        Dictionary<Currency, List<ExchangeRate>> _rates;

        public ExchangeRatesFromCurrency(Currency source)
        {
            Source = source;
        }

        public void FillFromTransfers(MoneyDataSet ds)
        {
            _rates = new Dictionary<Currency, List<ExchangeRate>>();

            foreach(Account a in ds.Accounts)
            {
                if (a.Currency != Source) { continue; }
                foreach(Transaction t in a.Transactions)
                {
                    if (t.TransferToTx == null) { continue; }

                    Currency dest = t.TransferToTx.Account.Currency;
                    if (dest == Source) { continue; }

                    List<ExchangeRate> destRates;
                    if (_rates.TryGetValue(dest, out destRates) == false)
                    {
                        destRates = new List<ExchangeRate>();
                        _rates.Add(dest, destRates);
                    }
                    destRates.Add(new ExchangeRate(t));
                }
            }

            // Sort the rates
            foreach (List<ExchangeRate> destRates in _rates.Values)
            {
                destRates.Sort();
            }
        }

        public ExchangeRate GetRate(Currency destCur, DateTime onDate)
        {
            if (destCur == Source)
            {
                return ExchangeRate.OneToOne(Source, destCur);
            }

            List<ExchangeRate> destRates;
            if (_rates.TryGetValue(destCur, out destRates) == false)
            {
                return ExchangeRate.OneToOne(Source, destCur);
            }

            if (destRates.Count == 0)
            {
                return ExchangeRate.OneToOne(Source, destCur);
            }

            ExchangeRate prevRate = destRates[0];

            // Special case -- tx before rates are known
            if (onDate <= prevRate.Start) 
            { 
                return prevRate; 
            }

            for (int i = 0; i < destRates.Count; i++)
            {
                ExchangeRate curRate = destRates[i];
                if (curRate.Start > onDate) 
                { 
                    return prevRate; 
                }
                prevRate = curRate;
            }

            // Return the last rate
            return prevRate;
        }

    }

}
